home *** CD-ROM | disk | FTP | other *** search
/ Clickx 115 / Clickx 115.iso / software / tools / windows / tails-i386-0.16.iso / live / filesystem.squashfs / usr / local / bin / tails-security-check < prev    next >
Encoding:
Text File  |  2013-01-06  |  5.0 KB  |  243 lines

  1. #! /usr/bin/perl
  2.  
  3. use strict;
  4. use warnings;
  5.  
  6. #man{{{
  7.  
  8. =head1 NAME
  9.  
  10. tails-security-check
  11.  
  12. =head1 VERSION
  13.  
  14. Version X.XX
  15.  
  16. =cut
  17.  
  18.  
  19. =head1 DESCRIPTION
  20.  
  21. =head1 SYNOPSIS
  22.  
  23. tails-security-check [ ATOM_FEED_BASE_URL [ BUILD_DATE ] ]
  24.  
  25.   ATOM_FEED_BASE_URL will be appended /index.XX.atom,
  26.   for XX in (current locale's language code, 'en'),
  27.   until success is reported by the HTTP layer.
  28.  
  29. =head1 AUTHOR
  30.  
  31. Tails dev team <amnesia@boum.org>
  32. See https://tails.boum.org/.
  33.  
  34. =cut
  35.  
  36. #}}}
  37.  
  38. use feature qw/say/;
  39. use Carp;
  40. use DateTime;
  41. use DateTime::Format::ISO8601;
  42. use Desktop::Notify;
  43. use Fatal qw( open close );
  44. use Locale::gettext;
  45. use POSIX;
  46. use XML::Atom;
  47. use XML::Atom::Feed;
  48.  
  49. ### Initialization
  50.  
  51. use IO::Socket::SSL;
  52. use Net::SSLeay;
  53. BEGIN {
  54.     IO::Socket::SSL::set_ctx_defaults(
  55.         verify_mode => Net::SSLeay->VERIFY_PEER(),
  56.         ca_file => '/etc/ssl/certs/UTN_USERFirst_Hardware_Root_CA.pem',
  57.     );
  58. }
  59. use LWP::UserAgent; # needs to be *after* IO::Socket::SSL's initialization
  60.  
  61. setlocale(LC_MESSAGES, "");
  62. textdomain("tails-security-check");
  63.  
  64. ### configuration
  65.  
  66. my $version_file = '/etc/amnesia/version';
  67. my $default_base_url = 'https://tails.boum.org/security/';
  68.  
  69. ### helper subs
  70.  
  71. =head2 build_date
  72.  
  73. Argument: file which the version information must be extracted from.
  74.  
  75. Returns a DateTime object that represents the build time extracted
  76. from the version file.
  77.  
  78. =cut
  79. sub build_date {
  80.     my $version_file = shift;
  81.  
  82.     open my $version_h, '<', $version_file;
  83.     my ($version, $date) = ( <$version_h> =~ /(.*) - (\d+)(?:\n)?$/ );
  84.     close $version_h;
  85.     if (!defined $date || !$date) {
  86.         croak gettext("Unparseable line in %s", $version_file);
  87.     }
  88.     return DateTime::Format::ISO8601->parse_datetime( $date );
  89. }
  90.  
  91. =head2 current_lang
  92.  
  93. Returns the two-letters language code of the current session.
  94.  
  95. =cut
  96. sub current_lang {
  97.     my ($code) = ($ENV{LANG} =~ m/([a-z]{2}).*/);
  98.  
  99.     return $code;
  100. }
  101.  
  102. =head2 atom_str
  103.  
  104. Argument: an Atom feed URL
  105.  
  106. Returns the Atom's feed content on success, undef on failure.
  107.  
  108. =cut
  109. sub atom_str {
  110.     my $url = shift;
  111.  
  112.     if (!defined $url) {
  113.         croak gettext("atom_str was passed an undefined argument");
  114.     }
  115.  
  116.     $ENV{HTTPS_VERSION} = 3;
  117.  
  118.     my $ua  = LWP::UserAgent->new;
  119.     $ua->proxy([qw(http https)] => 'socks://127.0.0.1:9062');
  120.     my $req = HTTP::Request->new('GET', $url);
  121.     my $res = $ua->request($req);
  122.     if (defined $res && $res->is_success) {
  123.         return $res->content;
  124.     }
  125.  
  126.     return undef;
  127. }
  128.  
  129. =head2 is_newer_than
  130.  
  131. Arguments: a XML::Atom::Entry, a DateTime object
  132.  
  133. Returns true if, and only if, the published field of the Atom entry is
  134. newer than the time represented by the DateTime object.
  135.  
  136. =cut
  137. sub is_newer_than {
  138.     my $entry = shift;
  139.     my $ref_dt = shift;
  140.  
  141.     my $entry_published_dt = DateTime::Format::ISO8601->parse_datetime($entry->published);
  142.     if (DateTime->compare( $entry_published_dt, $ref_dt) == 1) {
  143.         return 1;
  144.     }
  145.     return undef;
  146. }
  147.  
  148. =head2 get_new_entries
  149.  
  150. Arguments: the Atom feed URL, a DateTime reference object.
  151.  
  152. Returns the list of XML::Atom::Entry's, taken from the feed, that have
  153. been published after the reference time.
  154.  
  155. We use this poor man's manual Accept-Language algorithm as the website
  156. layout does not allow us to use content negotiation.
  157.  
  158. =cut
  159. sub get_new_entries {
  160.     my $base_url = shift;
  161.     my $since_dt = shift;
  162.  
  163.     my $separator = '';
  164.     $separator = '/' unless $base_url =~ m{/\z}xms;
  165.  
  166.     my @try_urls = (
  167.         $base_url . $separator . 'index.' . current_lang() . '.atom',
  168.         $base_url . $separator . 'index.en.atom',
  169.     );
  170.  
  171.     my $feed_str;
  172.     foreach my $url (@try_urls) {
  173.         last if ($feed_str = atom_str($url));
  174.     }
  175.  
  176.     if (!defined $feed_str) {
  177.         croak gettext("Empty fetched feed.");
  178.     }
  179.     my $feed = XML::Atom::Feed->new(\$feed_str);
  180.     return grep { is_newer_than($_, $since_dt) } $feed->entries();
  181. }
  182.  
  183. =head2 notify_user
  184.  
  185. Use the Desktop Notifications framework to notify the user about the
  186. Atom entries passed as arguments.
  187.  
  188. =cut
  189. sub notify_user {
  190.     my @entries = @_;
  191.  
  192.     my $notify = Desktop::Notify->new();
  193.  
  194.     my $summary = gettext('This version of Tails has known security issues:');
  195.     my $body = '';
  196.  
  197.     map { $body = $body
  198.               . '- '
  199.               . '<a href="'
  200.               . $_->id
  201.               . '">'
  202.               . $_->title
  203.               . '</a>'
  204.               . "\n";
  205.       } @entries;
  206.  
  207.     $notify->create(summary => $summary,
  208.                     body => $body,
  209.                     timeout => 0)->show();
  210. }
  211.  
  212. ### sanity checks
  213.  
  214. if (! -e "$version_file") {
  215.     die "The Tails version file ($version_file) does not exist."
  216. }
  217. if (! -r "$version_file") {
  218.     die "The Tails version file ($version_file) is not readable."
  219. }
  220.  
  221. ### parse command line args
  222.  
  223. my $base_url = shift || $default_base_url;
  224. my $opt_since = shift;
  225. my $since_dt;
  226. if (defined $opt_since) {
  227.     $since_dt = DateTime::Format::ISO8601->parse_datetime($opt_since);
  228. }
  229. else {
  230.     $since_dt = build_date($version_file);
  231. }
  232.  
  233. ### main
  234.  
  235. my @newer_entries = get_new_entries($base_url, $since_dt);
  236.  
  237. if (! @newer_entries) {
  238.     exit 0;
  239. }
  240. else {
  241.     notify_user(@newer_entries);
  242. }
  243.